package com.unknow.first.dapp.admin.account.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.unknow.first.dapp.admin.account.config.AddressAccountProperties;
import com.unknow.first.dapp.admin.account.constants.AccountConstants;
import com.unknow.first.dapp.admin.account.constants.AccountConstants.WithdrawChargeType;
import com.unknow.first.dapp.admin.account.mapper.AddressCoinConfig;
import com.unknow.first.dapp.admin.account.mapper.AddressCoinConfigMapper;
import com.unknow.first.dapp.admin.account.service.AddressCoinConfigService;
import com.unknow.first.dapp.admin.account.utils.Web3Utils;
import com.unknow.first.dapp.admin.account.vo.ChargeVO;
import org.cloud.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;

import java.math.BigDecimal;
import java.math.MathContext;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author Administrator
 * @description 针对表【t_address_coin_config】的数据库操作Service实现
 * @createDate 2022-05-17 19:49:00
 */
@Service
public class AddressCoinConfigServiceImpl extends ServiceImpl<AddressCoinConfigMapper, AddressCoinConfig> implements AddressCoinConfigService {

    @Autowired
    AddressAccountProperties addressAccountProperties;

    @Autowired
    AddressCoinConfigMapper addressCoinConfigMapper;

    @Override
    public List<AddressCoinConfig> getList(boolean forceValid) {
        QueryWrapper<AddressCoinConfig> queryWrapper = new QueryWrapper<>();
        if (forceValid) {
            queryWrapper.lambda().ne(AddressCoinConfig::getStatus, AccountConstants.CoinConfigStatus.FORBIDDEN.status);
        }
        return getBaseMapper().selectList(queryWrapper);
    }

    @Override
    public ChargeVO estimatedCharge(Integer coinId, BigDecimal amount) throws Exception {
        return estimatedCharge(coinId, new ArrayList<>(), amount);
    }

    @Override
    public ChargeVO estimatedCharge(Integer coinId, List<Address> middlePath, BigDecimal amount) throws Exception {
        ChargeVO chargeVO = new ChargeVO();
        QueryWrapper<AddressCoinConfig> coinConfigQueryWrapper = new QueryWrapper<>();
        coinConfigQueryWrapper.eq("coin_id", coinId);
        AddressCoinConfig addressCoinConfig = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
        Assert.isTrue(addressCoinConfig != null, "addressAccount.error.coinIdNoExists");
        BigDecimal chargeAmount;

        if (WithdrawChargeType.BY_PERCENT.value == addressCoinConfig.getWithdrawChargeType()) {
            chargeAmount = amount.multiply(addressCoinConfig.getWithdrawCharge(), MathContext.DECIMAL32).setScale(addressCoinConfig.getCoinDecimals());
        } else if (WithdrawChargeType.BY_FIX.value == addressCoinConfig.getWithdrawChargeType()) {
            chargeAmount = addressCoinConfig.getWithdrawCharge();
        } else {
            throw new BusinessException("address.account.error.charge.type.notSupport");  // 不支持的手续费类型
        }

        if (Objects.equals(addressCoinConfig.getCoinId(), addressCoinConfig.getChargeCoinId())) {
            chargeVO.setCharge(chargeAmount);
            chargeVO.setAddress(addressCoinConfig.getTokenContract());
            chargeVO.setCoinId(addressCoinConfig.getCoinId());
            chargeVO.setCoinName(addressCoinConfig.getCoinName());
            chargeVO.setCoinSymbol(addressCoinConfig.getCoinSymbol());
            return chargeVO;
        }
        final List<String> rpcUrl = addressAccountProperties.getRpcUrls();
        Web3j web3j = Web3j.build(new HttpService(rpcUrl.get(new SecureRandom().nextInt(rpcUrl.size()))));
        coinConfigQueryWrapper = new QueryWrapper<>();
        coinConfigQueryWrapper.eq("coin_id", addressCoinConfig.getChargeCoinId());
        AddressCoinConfig addressCoinConfigCharge = addressCoinConfigMapper.selectOne(coinConfigQueryWrapper);
        List<Address> routerPath = new ArrayList<>();
        routerPath.add(new Address(addressCoinConfig.getTokenContract()));
        routerPath.addAll(middlePath);
        routerPath.add(new Address(addressCoinConfigCharge.getTokenContract()));
        chargeAmount = Web3Utils.single()
            .getAmountsOutForRouter(web3j, addressAccountProperties.getRouterAddress(), new DynamicArray<>(Address.class, routerPath), chargeAmount);
        chargeVO.setCharge(chargeAmount);
        chargeVO.setAddress(addressCoinConfigCharge.getTokenContract());
        chargeVO.setCoinId(addressCoinConfigCharge.getCoinId());
        chargeVO.setCoinSymbol(addressCoinConfigCharge.getCoinSymbol());
        chargeVO.setCoinName(addressCoinConfigCharge.getCoinName());
        return chargeVO;
    }

    @Override
    public AddressCoinConfig getBySymbol(String symbol) {
        return getOne(
                Wrappers.lambdaQuery(AddressCoinConfig.class)
                        .eq(AddressCoinConfig::getCoinSymbol, symbol)
        );
    }

    @Override
    public AddressCoinConfig getByCoinId(Integer coinId) {
        return getOne(
                Wrappers.lambdaQuery(AddressCoinConfig.class)
                        .eq(AddressCoinConfig::getCoinId, coinId)
        );
    }
}




